#include <tls.h>

// #define dubug

using namespace std;
using namespace boost::asio;

inline unsigned short stream_get_short(ip::tcp::iostream& stream){
    unsigned char d1 = stream.get();
    unsigned char d2 = stream.get();
    unsigned short dout = d1 + d2 * 256;
    return dout;
}

void parse_extension(Extension& instance, ip::tcp::iostream& stream){
    instance.extension_type = (ExtensionType)stream_get_short(stream);
    unsigned short extension_data_length = stream_get_short(stream);
    instance.extension_data.resize(extension_data_length);
    if (extension_data_length > 0){
        for (int i = extension_data_length - 1;i >= 0; --i) {
            instance.extension_data[i] = stream.get();
        }
    }
    #ifdef dubug
    cout << "Get extension" << endl;
    #endif
}

vector<unsigned char> wrap_extension(Extension& instance){
    vector<unsigned char> return_vector(instance.extension_data);
    unsigned short length = return_vector.size();
    return_vector.push_back(length >> 8);
    return_vector.push_back(length % 256);
    unsigned short extension_type = (unsigned short)(instance.extension_type);
    return_vector.push_back(extension_type >> 8);
    return_vector.push_back(extension_type % 256);
    return return_vector;
}


void parse_clienthello(Hello& instance, ip::tcp::iostream& stream){
    instance.legacy_version = stream_get_short(stream);
    for (int i = 31;i >= 0; --i) {
        instance.random[i] = stream.get();
        #ifdef dubug
        cout << "Rand: " << (unsigned short)(instance.random[i]) << endl;
        #endif
    }
    unsigned short legacy_session_id_length = stream_get_short(stream);
    #ifdef dubug
    cout << "Get legacy_session_id_length"  << ": " << legacy_session_id_length << endl;
    #endif
    instance.legacy_session_id.resize(legacy_session_id_length);
    if (legacy_session_id_length > 0){
        for (int i = legacy_session_id_length - 1;i >= 0; --i) {
            instance.legacy_session_id[i] = stream.get();
        }
        #ifdef dubug
        cout << "Get legacy_session_id" << endl;
        #endif
    }
    unsigned short cipher_suites_length = stream_get_short(stream);
    #ifdef dubug
    cout << "Get cipher_suites_length"  << ": "  << cipher_suites_length << endl;
    #endif
    instance.cipher_suites.resize(cipher_suites_length);
    if (cipher_suites_length > 0){
        for (int i = cipher_suites_length - 1;i >= 0; --i) {
            instance.cipher_suites[i] = stream_get_short(stream);
        }
        #ifdef dubug
        cout << "Get cipher_suites" << endl;
        #endif
    }
    unsigned short legacy_compression_methods_length = stream_get_short(stream);
    instance.legacy_compression_methods.resize(legacy_compression_methods_length);
    #ifdef dubug
    cout << "Get legacy_compression_methods_length"  << ": "  << legacy_compression_methods_length << endl;
    #endif
    if (legacy_compression_methods_length > 0){
        for (int i = legacy_compression_methods_length - 1;i >= 0; --i) {
            instance.legacy_compression_methods[i] = stream.get();
        }
        #ifdef dubug
        cout << "Get legacy_compression_methods" << endl;
        #endif
    }

    unsigned short extensions_length = stream_get_short(stream);
    instance.extensions.resize(extensions_length);
    #ifdef dubug
    cout << "Get extensions_length"  << ": "  << extensions_length << endl;
    #endif
    if (extensions_length > 0){
        for (int i = extensions_length - 1;i >= 0; --i) {
            Extension ext_i;
            parse_extension(ext_i, stream);
            instance.extensions[i] = ext_i;
        }
    }
    #ifdef dubug
    cout << "Get parse_clienthello" << endl;
    #endif
}

vector<unsigned char> wrap_hello(Hello& instance){
    vector<unsigned char> return_vector;
    unsigned short length = instance.extensions.size() / sizeof(Extension);
    for (int i = length - 1; i >= 0; --i){
        vector<unsigned char> extension_vector = wrap_extension(instance.extensions[i]);
        return_vector.insert(return_vector.end(), extension_vector.begin(), extension_vector.end());
    }
    return_vector.push_back(length >> 8);
    return_vector.push_back(length % 256);
    return_vector.insert(return_vector.end(), instance.legacy_compression_methods.begin(), instance.legacy_compression_methods.end());
    length = instance.legacy_compression_methods.size();
    return_vector.push_back(length >> 8);
    return_vector.push_back(length % 256);
    return_vector.insert(return_vector.end(), instance.cipher_suites.begin(), instance.cipher_suites.end());
    length = instance.cipher_suites.size() / 2;
    return_vector.push_back(length >> 8);
    return_vector.push_back(length % 256);
    return_vector.insert(return_vector.end(), instance.legacy_session_id.begin(), instance.legacy_session_id.end());
    length = instance.legacy_session_id.size();
    return_vector.push_back(length >> 8);
    return_vector.push_back(length % 256);
    for (int i = 31; i >= 0; --i){
        return_vector.push_back(instance.random[i]);
    }
    return_vector.push_back(3);
    return_vector.push_back(3);
    return return_vector;
}

void parse_certificate_entry(CertificateEntry& instance, ip::tcp::iostream& stream){
    instance.certificate_type = (CertificateType)(stream.get());
    unsigned short data_length = stream_get_short(stream);
    instance.cert_data.resize(data_length);
    if (data_length > 0){
        for (int i = data_length - 1;i >= 0; --i) {
            instance.cert_data[i] = stream.get();
        }
    }
    #ifdef dubug
    cout << "Get certificate entry" << endl;
    #endif
}

vector<unsigned char> wrap_certificate_entry(CertificateEntry& instance){
    vector<unsigned char> return_vector;
    unsigned short length = instance.cert_data.size();
    for (int i = length - 1; i >= 0; --i){
        return_vector.push_back(instance.cert_data[i]);
    }
    return_vector.push_back(length >> 8);
    return_vector.push_back(length % 256);
    return_vector.push_back((unsigned char)instance.certificate_type);
    return return_vector;
}

void parse_certificate(Certificate& instance, ip::tcp::iostream& stream){
    unsigned short certificate_request_context_length = stream_get_short(stream);
    #ifdef dubug
    cout << "Get certificate_request_context_length"  << ": " << certificate_request_context_length << endl;
    #endif
    instance.certificate_request_context.resize(certificate_request_context_length);
    if (certificate_request_context_length > 0){
        for (int i = certificate_request_context_length - 1;i >= 0; --i) {
            instance.certificate_request_context[i] = stream.get();
        }
        #ifdef dubug
        cout << "Get certificate_request_context" << endl;
        #endif
    }
    unsigned short certificate_list_length = stream_get_short(stream);
    #ifdef dubug
    cout << "Get certificate_list_length"  << ": "  << certificate_list_length << endl;
    #endif
    instance.certificate_list.resize(certificate_list_length);
    if (certificate_list_length > 0){
        for (int i = certificate_list_length - 1;i >= 0; --i) {
            CertificateEntry ext_i;
            parse_certificate_entry(ext_i, stream);
            instance.certificate_list[i] = ext_i;
        }
    }
    #ifdef dubug
    cout << "Get certificate_list" << endl;
    #endif
}

vector<unsigned char> wrap_certificate(Certificate& instance){
    vector<unsigned char> return_vector;
    unsigned short length = instance.certificate_list.size();
    for (int i = length - 1; i >= 0; --i){
        vector<unsigned char> certificate_list_vector = wrap_certificate_entry(instance.certificate_list[i]);
        return_vector.insert(return_vector.end(), certificate_list_vector.begin(), certificate_list_vector.end());
    }
    return_vector.push_back(length >> 8);
    return_vector.push_back(length % 256);
    return_vector.insert(return_vector.end(), instance.certificate_request_context.begin(), instance.certificate_request_context.end());
    length = instance.certificate_request_context.size();
    return_vector.push_back(length >> 8);
    return_vector.push_back(length % 256);
    return return_vector;
}

void parse_keyshare(KeyShareEntry& instance, ip::tcp::iostream& stream){
    instance.group = stream_get_short(stream);
    unsigned short data_length = stream_get_short(stream);
    instance.key_exchange.resize(data_length);
    if (data_length > 0){
        for (int i = data_length - 1;i >= 0; --i) {
            instance.key_exchange[i] = stream.get();
        }
    }
    #ifdef dubug
    cout << "Get keyshare" << endl;
    #endif
}

vector<unsigned char> wrap_keyshare(KeyShareEntry& instance, bool inverse){
    vector<unsigned char> return_vector;
    unsigned short length = instance.key_exchange.size();
    #ifdef dubug
    cout << "Get key_exchange_length"  << ": " << length << endl;
    #endif
    if (inverse){
        for (int i = 0; i <= length - 1; ++i){
            return_vector.push_back(instance.key_exchange[i]);
        }
    } else {
        for (int i = length - 1; i >= 0; --i){
            return_vector.push_back(instance.key_exchange[i]);
        }
    }
    return_vector.push_back(length >> 8);
    return_vector.push_back(length % 256);
    return_vector.push_back(instance.group >> 8);
    return_vector.push_back(instance.group % 256);
    return return_vector;
}

void parse_payload(Payload& instance, ip::tcp::iostream& stream){
    unsigned short data_length = stream_get_short(stream);
    instance.payload.resize(data_length);
    if (data_length > 0){
        for (int i = data_length - 1;i >= 0; --i) {
            instance.payload[i] = stream.get();
        }
    }
    #ifdef dubug
    cout << "Get payload" << endl;
    #endif
}

vector<unsigned char> wrap_payload(Payload& instance, bool inverse){
    vector<unsigned char> return_vector;
    unsigned short length = instance.payload.size();
    #ifdef dubug
    cout << "Get payload_length"  << ": " << length << endl;
    #endif
    if (inverse){
        for (int i = 0; i <= length - 1; ++i){
            return_vector.push_back(instance.payload[i]);
        }
    } else {
        for (int i = length - 1; i >= 0; --i){
            return_vector.push_back(instance.payload[i]);
        }
    }
    return_vector.push_back(length >> 8);
    return_vector.push_back(length % 256);
    return return_vector;
}
